home *** CD-ROM | disk | FTP | other *** search
Wrap
//============================================================================== // Scn24p3: AI Scenario Script for scenario 24 player 3 //============================================================================== /* AI owner: Dave Leary Scenario owner: Dave Leary The AI for Circe and friends in scenario 24. *** DIFFICULTY LEVEL NOTES *** Easy level - Even more towers removed. Guards near Circe's fortress removed. Fewer researches. No archers included in attacks. Moderate level - Base level. Difficult level - Towers around Circe's fortress. Additional researches. Petroboli maintained and added to infantry attack groups. Nightmare - Lots of towers around Circe's fortress. Additional researches. Petroboli maintained and added to infantry attack groups. */ //============================================================================== // Difficulty Level check predeclared. int difflevel=-1; //============================================================================== // Set Town Location //============================================================================== void setTownLocation(void) { //Look for the "Town Location" marker. kbSetTownLocation(kbGetBlockPosition("20871")); } //============================================================================== // Basic startup //============================================================================== void miscStartup(void) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Startup message(s). aiEcho(""); aiEcho(""); aiEcho("Scn24P3 AI Start, filename='"+cFilename+"'."); //Spit out the map size. aiEcho(" Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+")."); aiEcho("Difficulty Level="+difflevel+"."); //Cheat like a bastard. Once only, though. kbLookAtAllUnitsOnMap(); //Calculate some areas. kbAreaCalculate(1200.0); //Set our town location. setTownLocation(); //Reset random seed aiRandSetSeed(); //Allocate all resources to the root escrow by setting percentage of military/economy to 0. kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 ); kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 ); //Allocate all resources to the root escrow. kbEscrowAllocateCurrentResources(); // Drop the AI attack response distance for this player to 3 meters. We'll up it later. aiSetAttackResponseDistance(3.0); } //============================================================================== //============================================================================== // Attack stuff. //============================================================================== //============================================================================== //Shared variables. int numberAttacks=0; int attackPlayerID=-1; //TODO: Decide how to rep attack group size. int attackMinimumGroupSize=4; int attackMaximumGroupSize=6; //Attack 1 vars. int attackPlan1ID=-1; int maintainPlan1ID=-1; //Attack 2 vars. int attackPlan2ID=-1; int maintainPlan2ID=-1; // Route and path vars int attackRoute1ID=-1; int attackPath1ID=-1; int attackRoute2ID=-1; int attackPath2ID=-1; // Unit type(s) int attackerUnitTypeID1=cUnitTypeHypaspist; int attackerUnitTypeID2=cUnitTypeHippikon; int attackerUnitTypeID3=cUnitTypeToxotes; int siegeUnitTypeID1=cUnitTypePetrobolos; //========================================================================================= // Kidd's cool configQuery function: used to create attack routes, etc. Oooh, lovin' that! //========================================================================================= bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 ) { if ( queryID == -1) { return(false); } if (player != -1) kbUnitQuerySetPlayerID(queryID, player); if (unitType != -1) kbUnitQuerySetUnitType(queryID, unitType); if (action != -1) kbUnitQuerySetActionType(queryID, action); if (state != -1) kbUnitQuerySetState(queryID, state); if (center != vector(-1,-1,-1)) { kbUnitQuerySetPosition(queryID, center); if (sort == true) kbUnitQuerySetAscendingSort(queryID, true); if (radius != -1) kbUnitQuerySetMaximumDistance(queryID, radius); } return(true); } //============================================================================== // initAttack: Creates attack routes, etc. //============================================================================== void initAttack(int playerID=-1) { //Destroy all previous attacks (if this isn't the player we're already attacking. if (playerID != attackPlayerID) { //Reset the attack player ID. attackPlayerID=-1; //Destroy any previous attack plan. aiPlanDestroy(attackPlan1ID); attackPlan1ID=-1; aiPlanDestroy(attackPlan2ID); attackPlan2ID=-1; //Destroy our previous attack paths. kbPathDestroy(attackPath1ID); attackPath1ID=-1; kbPathDestroy(attackPath2ID); attackPath2ID=-1; //Destroy our previous attack routes. attackRoute1ID=-1; attackRoute2ID=-1; //Reset the number of attacks. numberAttacks=0; } //Save the player to attack. attackPlayerID=playerID; vector gatherPointSouth=kbGetBlockPosition("20871"); vector gatherPointNorth=kbGetBlockPosition("20873"); //Setup attack path 1 - for south group attackPath1ID=kbPathCreate("Attack Path 1"); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("20872")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("20874")); //Create attack route 1. attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPointSouth, kbGetBlockPosition("20875")); if (attackRoute1ID >= 0) kbAttackRouteAddPath(attackRoute1ID, attackPath1ID); //Setup attack path 2 - go right attackPath2ID=kbPathCreate("Attack Path 2"); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("20872")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("20874")); //Create attack route 2. attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPointSouth, kbGetBlockPosition("20875")); if (attackRoute2ID >= 0) kbAttackRouteAddPath(attackRoute2ID, attackPath2ID); } //============================================================================== // setupFirstAttack (hypaspists) //============================================================================== bool setupFirstAttack(int playerID=-1) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Info. aiEcho("Attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPoint=kbGetBlockPosition("20871"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); //Attack route - always the southern route aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0); //Set up the attack route usage pattern. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); //Add the unit types to the plan. Base unit = hypaspists. Also add a couple of toxotoes. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); if ( difflevel > 0 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 2, 4); } // When the difficulty level is greater than moderate, add petroboli if available. if ( difflevel > 1 ) { aiPlanAddUnitType(newAttackPlanID, siegeUnitTypeID1, 0, 2, 2); } //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //Plan requires all need units to work (can be false). aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately. aiPlanSetOrphan(attackPlan1ID, true); attackPlan1ID=newAttackPlanID; //Increment our overall number of attacks. numberAttacks++; } //============================================================================== // setupSecondAttack (hippikons) //============================================================================== bool setupSecondAttack(int playerID=-1) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Info. aiEcho("Attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPoint=kbGetBlockPosition("20873"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack. if ( difflevel > 1 ) { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); } else { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); } //Attack route - always the northern route aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0); //Set up the attack route usage pattern. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); //Add the unit types to the plan. Base unit = hippikons. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //Plan requires all need units to work (can be false). aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately. aiPlanSetOrphan(attackPlan1ID, true); attackPlan1ID=newAttackPlanID; //Increment our overall number of attacks. numberAttacks++; } //============================================================================== // Attack Generator 1 - base unit, hypaspist //============================================================================== rule attackGenerator1 minInterval 90 inactive group AttackRules runImmediately { //See how many "idle" attack plans we have. Don't create any more if we have //idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; //If we have enough unassigned military units, create a new attack plan. int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1); aiEcho("There are "+numberAvailableUnits+" hypaspists available for a new attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupFirstAttack(1); } //============================================================================== // Attack Generator 2 - base unit, hippikons //============================================================================== rule attackGenerator2 minInterval 135 inactive group AttackRules runImmediately { //See how many "idle" attack plans we have. Don't create any more if we have //idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; //If we have enough unassigned military units, create a new attack plan. int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2); aiEcho("There are "+numberAvailableUnits+" hippikons available for a new attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupSecondAttack(1); } //============================================================================== // Attack enablers - enable attacks after initial timers expire //============================================================================== rule attack1Enabler minInterval 120 inactive group AttackRules { xsEnableRule("attackGenerator1"); xsDisableSelf(); } rule attack2Enabler minInterval 150 inactive group AttackRules { xsEnableRule("attackGenerator2"); xsDisableSelf(); } //============================================================================== // Tech Researching Rules - all activated upon arrival at temple. //============================================================================== // Medium Infantry five minutes after arrival rule researchMediumInfantry minInterval 300 inactive { int planID=aiPlanCreate("Researching Medium Infantry.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumInfantry); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy); aiPlanSetActive(planID); aiEcho("Researching medium cavalry."); //Done. xsDisableSelf(); } // Medium Cavalry seven minutes after arrival rule researchMediumCavalry minInterval 420 inactive { int planID=aiPlanCreate("Researching Medium Cavalry.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumCavalry); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeStable); aiPlanSetActive(planID); aiEcho("Researching medium cavalry."); //Done. xsDisableSelf(); } // Medium Archers seven minutes after arrival rule researchMediumArchers minInterval 420 inactive { int planID=aiPlanCreate("Researching Medium Archers.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumArchers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArcheryRange); aiPlanSetActive(planID); aiEcho("Researching medium cavalry."); //Done. xsDisableSelf(); } // Heavy Infantry eight minutes after arrival. rule researchHeavyInfantry minInterval 480 inactive { int planID=aiPlanCreate("Researching Heavy Infantry.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy); aiPlanSetActive(planID); aiEcho("Researching heavy infantry."); //Done. xsDisableSelf(); } // Bronze weapons eight minutes after arrival. rule researchBronzeWeapons minInterval 480 active { int planID=aiPlanCreate("Bronze weapons research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeWeapons); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } // Heavy Archers nine minutes after arrival. rule researchHeavyArchers minInterval 540 inactive { int planID=aiPlanCreate("Researching Heavy Infantry.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArcheryRange); aiPlanSetActive(planID); aiEcho("Researching heavy archers."); //Done. xsDisableSelf(); } // Bronze shields ten minutes after arrival rule researchBronzeShields minInterval 600 inactive { int planID=aiPlanCreate("Bronze shields research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeShields); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } // Watchtower research ten minutes after arrival. rule researchWatchtower minInterval 600 inactive { int planID=aiPlanCreate("Watchtower research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechWatchTower); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeTower); aiPlanSetActive(planID); //Done. xsDisableSelf(); } // Boiling Oil research, twelve minutes after arrival rule researchBoilingOil minInterval 720 inactive { int planID=aiPlanCreate("Boiling Oil research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBoilingOil); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeTower); aiPlanSetActive(planID); //Done. xsDisableSelf(); } // This rule helps bulletproof the "all my pigs get slaughtered en route" bug. // It ups response distance 60 seconds after Arkantos gets to the temple. // // At this point, if the pigs are still trailing miles behind, the player deserves // to lose all his piggies, and if he does, I will point and laugh. rule responseDistanceSet minInterval 60 inactive { aiSetAttackResponseDistance(40.0); xsDisableSelf(); } //===================================================================================== // Attack (and research) Enabler. This is called from the scenario with an AI Func effect // when Arkantos reaches the Zeus temple. //===================================================================================== void attackEnabler(int scriptCall = -1) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); aiEcho("*** AI: Arkantos has reached the Temple of Zeus. ***"); aiEcho("*** Attacks and research now enabled. ***"); xsEnableRule("attack1Enabler"); xsEnableRule("attack2Enabler"); xsEnableRule("researchWatchtower"); xsEnableRule("responseDistanceSet"); if ( difflevel > 0 ) { xsEnableRule("researchMediumInfantry"); xsEnableRule("researchMediumArchers"); xsEnableRule("researchBronzeWeapons"); xsEnableRule("researchHeavyInfantry"); xsEnableRule("researchBoilingOil"); } if ( difflevel > 1 ) { xsEnableRule("researchMediumCavalry"); xsEnableRule("researchBronzeShields"); xsEnableRule("researchHeavyArchers"); } } //============================================================================== // MAIN. //============================================================================== void main(void) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Startup. miscStartup(); if ( difflevel == 0 ) { xsSetRuleMinInterval( "attack1Enabler", 240 ); xsSetRuleMinInterval( "attack2Enabler", 360 ); xsSetRuleMinInterval( "attack1Generator", 210 ); xsSetRuleMinInterval( "attack2Generator", 300 ); } if ( difflevel > 0 ) { attackMinimumGroupSize=5; attackMaximumGroupSize=7; } if ( difflevel == 3 ) { attackMinimumGroupSize=8; attackMaximumGroupSize=10; } //Share the number to maintain. int numberToMaintain=attackMinimumGroupSize*2; //Share a common gather point. vector gatherPointSouth=kbGetBlockPosition("20871"); vector gatherPointNorth=kbGetBlockPosition("20873"); vector gatherPointSiege=kbGetBlockPosition("22462"); // Maintain X hypaspists. int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain); if (maintainPlan1ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 30 seconds aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 30); //Set a gather point. aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointSouth); //Activate the plan. aiPlanSetActive(maintainPlan1ID); } // Maintain X hippikons. int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain); if (maintainPlan2ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 45 seconds aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 45); //Set a gather point. aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointNorth); //Activate the plan. aiPlanSetActive(maintainPlan2ID); } // Maintain 4 toxotes. int maintainPlan3ID=aiPlanCreate("Maintain 4 "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain); if (maintainPlan3ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 50 seconds aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 50); //Set a gather point. aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointSouth); //Activate the plan. aiPlanSetActive(maintainPlan3ID); } // On higher difficulty levels, build siege equipment. if ( difflevel > 1 ) { // Maintain 2 petroboli. int maintainPlan4ID=aiPlanCreate("Maintain 2 "+kbGetProtoUnitName(siegeUnitTypeID1), cPlanTrain); if (maintainPlan4ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, siegeUnitTypeID1); //Total of 12 - if the player survives this, the AI stops makin' 'em. aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToTrain, 0, 12); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, 2); //Don't train units faster than every 90 seconds aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 90); //Set a gather point. aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointSiege); //Activate the plan. aiPlanSetActive(maintainPlan4ID); } } }